package com.thghh.gkrpc;

import com.tghhh.gkrpc.Request;
import com.tghhh.gkrpc.Response;
import com.thghh.gkrpc.codec.Decoder;
import com.thghh.gkrpc.codec.Encoder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @author Zhikang.Peng
 * @version 1.0
 * @email thghh@qq.com
 * @date 2020/4/14 11:28
 */
@Data
@Slf4j
public class RpcServer {
    private RpcServerConfig config;
    private TransportServer net;
    private Encoder encoder;
    private Decoder decoder;
    private ServiceManager serviceManager;
    private ServiceInvoker serviceInvoker;

    /**
     * <p>一个匿名类，用于处理客户端发起动远程调用，处理逻辑为根据客户端发送过来的字节流反序列化成{@link Request}对象,
     * {@link Request}保存了服务方法的描述{@link Request#service}作为key从{@link ServiceManager#lookupService(Request)}
     * 获取具体的方法服务实例，然后调用真正的方法，并把方法的返回结果放入{@link Response}对象并序列化成字节放入输入流返回给客户端
     * </p>
     */
    private RequestHandler handler = new RequestHandler() {
        @Override
        public void onRequest(InputStream input, OutputStream output) {
            Response response = new Response();
            try {
                // 从输入流中获取客户端的数据，反序列化成Request对象
                byte[] inBytes = IOUtils.readFully(input, input.available());
                Request request = decoder.decode(inBytes, Request.class);
                log.info("get request: {}", request);
                // 从服务管理对象中获取服务实例，并调用本地方法，将结果放入Response中
                ServiceInstance serviceInstance = serviceManager.lookupService(request);
                Object returnObject = serviceInvoker.invoke(serviceInstance, request);
                response.setData(returnObject);
            } catch (IOException e) {
                log.warn(e.getMessage(), e);
                response.setCode(1);
                response.setMessage("rpc service got error: " + e.getClass().getName() + " : " + e.getMessage());
            } finally {
                try {
                    // 将Response序列化成字节流，返回给客户端
                    byte[] outBytes = encoder.encode(response);
                    output.write(outBytes);

                    log.info("response client");
                } catch (IOException e) {
                    log.warn(e.getMessage(), e);
                }
            }
        }
    };

    public RpcServer() {
        this(new RpcServerConfig());
    }

    public RpcServer(RpcServerConfig config) {
        this.config = config;

        // net
        this.net = ReflectionUtils.newInstance(config.getTransportClass());

        //codes
        this.encoder = ReflectionUtils.newInstance(config.getEncoder());
        this.decoder = ReflectionUtils.newInstance(config.getDecoder());

        // service
        this.serviceManager = new ServiceManager();
        this.serviceInvoker = new ServiceInvoker();

        this.net.init(config.getPort(), handler);
    }

    /**
     * 注册服务
     *
     * @param interfaceClass
     * @param bean
     * @param <T>
     */
    public <T> void registerService(Class<T> interfaceClass, T bean) {
        serviceManager.registerService(interfaceClass, bean);
    }

    public void start() {
        this.net.start();
    }

    public void stop() {
        this.net.stop();
    }
}
